import dataclasses
from functools import wraps


class NameSpace(dict):
    immutable_keywords = {'_allow_update'}

    def __init__(self, _allow_update=True, **kwargs):
        super().__init__(**kwargs)
        self._allow_update = _allow_update

    def register(self, name):
        def register_decorator(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                return f(*args, **kwargs)

            self[name] = f
            return wrapper

        return register_decorator

    def __setitem__(self, key, value):
        if key in self and not self._allow_update:
            raise KeyError(f'name {repr(key)} already exists')
        return super().__setitem__(key, value)

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        if key in self.__class__.immutable_keywords:
            return super().__setattr__(key, value)
        self[key] = value


@dataclasses.dataclass
class BaseDataclass:
    def as_dict(self):
        return {
            field.name: getattr(self, field.name)
            for field in dataclasses.fields(self)
        }
